// This is the main DLL file.

#include "stdafx.h"
#define DLLAPI_BUTTON_HOOK
#include "ButtonHook.h"
#include <psapi.h>

using namespace Ooganizer;

#ifdef _DEBUG
	#import "..\\..\\..\\Target\\Debug\\Platform\\Ooganizer.Platform.OoganizerSettings.tlb" raw_interfaces_only
#else
	#import "..\\..\\..\\Target\\Release\\Platform\\Ooganizer.Platform.OoganizerSettings.tlb" raw_interfaces_only
#endif
using namespace Ooganizer_Platform_OoganizerSettings;

#ifdef _DEBUG
	#import "..\\..\\..\\Target\\Debug\\Platform\\Ooganizer.Platform.Platform.tlb" raw_interfaces_only
#else
	#import "..\\..\\..\\Target\\Release\\Platform\\Ooganizer.Platform.Platform.tlb" raw_interfaces_only
#endif
using namespace Ooganizer_Platform_Platform;

#ifdef _DEBUG
	#import "..\\..\\..\\Target\\Debug\\ButtonWPForm.tlb"
#else
	#import "..\\..\\..\\Target\\Release\\ButtonWPForm.tlb"
#endif
using namespace ButtonWPForm;

////
// ***** What type of Windows should have the Button *****
////
const DWORD WS_STYLES_TO_TEST_FOR = WS_CAPTION | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU;

//////////////////////////////////////////////////////////////////////
//******************************************************************** 
// Macros and typedefs
//******************************************************************** 
//////////////////////////////////////////////////////////////////////

#define MSG_HOOK			( TEXT( "MSG_HOOK" ) )
#define MSG_UNHOOK			( TEXT( "MSG_UNHOOK" ) )

////
// Each DLL instance (All Instances) need to keeps track of the following
// Data members (these end up being the same values so no need to put them into the DS)
////
HINSTANCE s_hModule = NULL;
UINT m_WMHook = NULL;
UINT m_WMUnHook = NULL;

// enum to send MSG_HOOK and MSG_UNHOOK
typedef enum _MSG{_MSG_HOOK,_MSG_UNHOOK} MSG_;

////
// Each DLL instance (All instances not just the once whose window,
// we'll hook into) will hold a pointer to it's own 
// CWHPaccess Object. (allowing it access to the Shared Data Segment)
////
CWHPaccess* s_ButtonHookAccess = NULL;

////
// Each DLL instanc needs to keep track of it's recently closed windows
////
#define NCLOSES_TO_TRACK 25
// timeout values, we need to ignore activate messages for nTime when
// we unhook to avoid rehooking again in processes that have multiple windows
const int IGNORE_ACTIVATE_MESSAGE_TIMEOUT_MS = 150;
struct RecentWindowsWTimeouts
{
	HWND hWnd;
	DWORD dwTimeInserted;
};
RecentWindowsWTimeouts s_hRecentClosedWindows[NCLOSES_TO_TRACK] = {NULL,NULL};
UINT s_HRecentListI = 0;

//////////////////////////////////////////////////////////////////////
// Func: InsertRecentClosedWindow()
// Desc: Insert a recently closed window
//////////////////////////////////////////////////////////////////////
void InsertRecentClosedWindow(HWND hWnd)
{
  RecentWindowsWTimeouts recent;
  recent.hWnd = hWnd;
  recent.dwTimeInserted = GetTickCount();

  s_hRecentClosedWindows[s_HRecentListI] = recent;
  s_HRecentListI = s_HRecentListI + 1;

  if(s_HRecentListI == NCLOSES_TO_TRACK)
	 s_HRecentListI = 0;
}
//////////////////////////////////////////////////////////////////////
// Func: FindRecentClosedWindow()
// Desc: Find a recently closed window
//////////////////////////////////////////////////////////////////////
BOOL FindRecentClosedWindow(HWND hWnd)
{
	DWORD dwTickCount = GetTickCount();
	for (UINT i = 0; i < s_HRecentListI; ++i)
	{
		if(s_hRecentClosedWindows[i].hWnd == hWnd)
		{
			DWORD diffTime = dwTickCount - s_hRecentClosedWindows[i].dwTimeInserted;
			if(diffTime < IGNORE_ACTIVATE_MESSAGE_TIMEOUT_MS)
				return true;
			else
				return false;
		}
	}
	return false;
}
//////////////////////////////////////////////////////////////////////
// Func: EnumProc()
// Desc: Hook/Unhook all top level windows. Called for every top-level
//		 Window on the screen. 
//
// Prms: lParam, if true send Hook Message, false send UnHook Msg
//       
// Retr: TRUE, always
//////////////////////////////////////////////////////////////////////
BOOL CALLBACK EnumProc( HWND hWnd, LPARAM lParam )
{
	switch (lParam)
	{
	case _MSG_HOOK:		
		SendMessage( hWnd, m_WMHook, 0, 0 );				
		break;
	
	case _MSG_UNHOOK:
		// log(hWnd, "Sending Unhook Msg to");
		SendMessage (hWnd, m_WMUnHook, 0, 0 );
		break;
	
	default:
		break;
	}

	return TRUE;
}
//////////////////////////////////////////////////////////////////////
// Func: GetProcessNameFromPID()
// Retr: Returns the process name in the buffer and true if everything went well,
//	     false otherwise.
//////////////////////////////////////////////////////////////////////
bool GetProcessNameFromHWND(HWND hwnd, char strProcessNameBuf[MAX_PATH + 1])
{
	DWORD pid = NULL;
	GetWindowThreadProcessId(hwnd,&pid);
    HANDLE hProcess = OpenProcess( PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, pid );

    // Get the process name
    if (NULL != hProcess )
        GetModuleBaseNameA( hProcess, NULL, strProcessNameBuf, (MAX_PATH + 1));

	//#if defined _DEBUG
		//log(LOGGING_HIGH,"%s  (PID: %u)", strProcessNameBuf, pid);
	//#endif

    CloseHandle(hProcess);
	return true;
}
//////////////////////////////////////////////////////////////////////
// Func: IsWhiteListed()
// Desc: Function allows us to Blacklist certain windows to not be
//		 included when hooking the WinProcButton Hook.
//		 a Window can be whitelisted either on Title or Type, passed in
//		 thru the configuration
//
// Prms: hwnd, handle to a window we want to check
//       
// Retr: true, if window is WhiteListed, false otherwise
//////////////////////////////////////////////////////////////////////
bool IsWhiteListed(HWND hwnd)
{			
	char strBuf[MAX_PATH + 1] = {0};

	if(GetProcessNameFromHWND(hwnd,strBuf))
	{
		_strupr_s(strBuf);
		if(s_ButtonHookAccess->IsAllowedProcessName(strBuf))
			return true;
	}

	if (GetWindowTextA(hwnd,strBuf,MAX_PATH))				
	{			
		_strupr_s(strBuf);			
		if(s_ButtonHookAccess->IsAllowedWindowsTitle(strBuf))		
			return true;
	}	

	if(GetClassNameA(hwnd,strBuf,MAX_PATH))
	{
		_strupr_s(strBuf);
		if(s_ButtonHookAccess->IsAllowedWindowsClass(strBuf))
			return true;
	}

	return false;
}
//////////////////////////////////////////////////////////////////////
// Func: IsAWindowToHookInto()
// Desc: Broad check to see if we want to hook into a window.
//		 Called by CallWndHookProc. 
//		 Window on the screen. 
//
// Prms: hwnd, handle to a window we want to check//       
// 
// Note: This function calls IsBlackListed() for more window exclusions.
// 
// Retr: true, if window is should be hooked into, false otherwise
//////////////////////////////////////////////////////////////////////
bool IsAWindowToHookInto(HWND hwnd)
{	
	DWORD dwStyle = GetWindowLong(hwnd, GWL_STYLE);
	DWORD dwExStyle = GetWindowLong(hwnd, GWL_EXSTYLE);		
			
	if (    /* top-raised windows */
			( ( dwExStyle & WS_EX_WINDOWEDGE ) == WS_EX_WINDOWEDGE )

		    /* Styles to test for (named above) */
		    && ( ( dwStyle & WS_STYLES_TO_TEST_FOR ) == WS_STYLES_TO_TEST_FOR ) 

		    /* Has no parent = top-level window */	 
		    && ( GetParent(hwnd) == NULL )									 

			/* Check if Window is not already hooked into*/
			&& ( s_ButtonHookAccess->FindHookedWnd(hwnd) == FALSE )

			/* Check if Window has not been recently closed in this process*/
			&& ( FindRecentClosedWindow(hwnd) == FALSE )

			// **** CUSTOM INCLUSION LIST  ****
	        && ( IsWhiteListed(hwnd) )

			// Make sure the window is visible to begin with * bug fix for ms word*
			&& ( ::IsWindowVisible(hwnd))
	   )
	{
		return true;
	}
	else
	{
		return false;
	}
}
//////////////////////////////////////////////////////////////////////
// Func: HookWndProc()
// Desc: Function that implements the WndProc for every process that
//		 has the window we are looking for. This is code is shared within
//		 the process on every window in the system.
//        
// Note: This function is just the WndProc() Implementation
//	     All we have to do is process it if we care for it or pass it down
//		 to the next hook (that may be in the system).
//
// Prms:  hWnd, handle to window
//		  Msg, passed in message
//		  wParam, specifies additional message information
//		  lParam, specifies additional message information
//              
// Retr: LRESULT, 
//////////////////////////////////////////////////////////////////////
LRESULT CALLBACK MyWndProc( HWND hWnd, UINT Msg, WPARAM wParam, LPARAM lParam )
{	

	// default return
	LRESULT result = 0;
	
	// Get the HookWndItem Information
	BLANK_HookWndItem(item);
	item = s_ButtonHookAccess->GetHookedWndItem(hWnd);
	
	bool bCloseMsg = ((Msg == WM_NCDESTROY) || (Msg == WM_DESTROY) || (Msg == WM_CLOSE));
	if(bCloseMsg)
	   log(LOGGING_MEDIUM,"Received WM_NCDESTROY or DESTROY MESSAGE or WM_CLOSE for window %d", item.hWnd);

	// Is this is a Window that is hooked?
	if((item.hWnd != NULL) && (hWnd = item.hWnd)) 
	{
		////
		// Create wpfcaller object  for this Window, if it doesn't exist
		// ~Every wpfcaller is mapped to a single HookWndItem
		////				
		if(item.wpfcaller == NULL)
		{	
			log(LOGGING_LOW,"Creating a wpfCaller Instance");
			item.wpfcaller = new WPFCaller(item, s_hModule);	
		}

		////
		// Case StopHook wants us to Unhook the WmProc Hook or
		// the Window gets Closed
		////			
		if ( (Msg == m_WMUnHook) ||	 
			  bCloseMsg )
		{
			log(LOGGING_LOW, "Received WMUnook or bCloseMsg message");
			
			// No Longer need to monitor this Window
			s_ButtonHookAccess->DeleteHookedWnd(item.hWnd);
			InsertRecentClosedWindow(item.hWnd);

			// Unhook Our WndProc Procedure (put the old one back)
			log(LOGGING_MEDIUM, "Unhooking a Window from the HookWndProc");
			SetWindowLong(hWnd,GWL_WNDPROC,(LONG) item.DefWndProc);

			// Delete this Window's CaptionButton Object
			if(item.wpfcaller != NULL)
			{				
				log(LOGGING_MEDIUM, "Deleting a wpfCaller Instance");
				delete item.wpfcaller;
			}

			// Call the old WindProc to handle Close message
			if(bCloseMsg)
				result = CallWindowProc( item.DefWndProc, hWnd, Msg, wParam, lParam );					
		}
		// All other Messages
		else
		{				
			// FOR TESTING - Skip Handling of Messages (Below)		
			// result = CallWindowProc( item->DefWndProc, hWnd, Msg, wParam, lParam );
			try
			{
				// the wpfCaller Handles all other Messages
				if(item.wpfcaller != NULL)
					result = item.wpfcaller->OnDefault(hWnd, Msg, wParam, lParam);
				else
					CallWindowProc(item.DefWndProc, hWnd, Msg, wParam, lParam);
			}
			catch(...)
			{
				log(LOGGING_LOW,"*Error* Occured calling WpfCaller->OnDefault()");
			}
		}
	}
	
	return result;
}
//////////////////////////////////////////////////////////////////////
// Func: CallWndHookProc()
// Desc: Function that will replace the normal WndProc with the HookWndProc 
//		 procedure. It will do that for every process on the system that 
//		 has a window. It will assign the function HookWndProc()
//       
// Prms:  nCode [in]  = if == HC_ACTION CallWndHookProc must process the message
//		  wParam [in] = specifies if message is send by current process 
//						non-zero if it is current process, null otherwise
//		  LPARAM [in] = pointer to a CWPRETSTRUCT that contains details about the message
//
// Note: see MSDN for CallWndRetProc Function passed in with SetWindowHookEx
//		 ~We don't check wParam (pass directly thru) because we want to process all messages
//		  for all processes and don't care where it originates
//       
// IMP!: In this function we check the Window and decide whether to display the button or not
//		 ~!IMP) WH_CALLWNDPROC hook is called in the context of the thread that calls SendMessage
// 
// Retr: LRESULT, 
//////////////////////////////////////////////////////////////////////
LRESULT WINAPI CallWndHookProc(int nCode, WPARAM wParam, LPARAM lParam)
{

	if (nCode == HC_ACTION)
	{
		LPCWPSTRUCT pCwp = (LPCWPSTRUCT) lParam;				
		
		// Do we care about this Window?, We don't want to hook into all Windows
		if(IsAWindowToHookInto(pCwp->hwnd)) 
		{				

			// Do we care about this Message?
			if((pCwp->message == m_WMHook) /* We'll receive the Hook Message StartButtonHook is called */
			   || (pCwp->message == WM_NCACTIVATE)) /* Activates & deactivates a window, caption is redrawn. */
		    {
				
				// Create a hook Item, We are ready to hook into this Window
				HookWndItem item;
				
				// hWnd = the handle to the current window
				// DefWndProc = handle of the this window procedure (the address that is replaced by our Hook Function)
				// HookWndProc = the address of this DLL instance's HookWndProc Function
				item.hWnd = pCwp->hwnd;  								
				item.DefWndProc = reinterpret_cast<WNDPROC>(GetWindowLong(pCwp->hwnd,GWL_WNDPROC)); 
				item.HookWndProc = MyWndProc;
				item.wpfcaller = NULL; // The Caption Button will be initialized by MyHookWnd				
				
				// Add the hook Item to the Global List
				s_ButtonHookAccess->InsertHookedWndItem(item);

				////
				// ********************** INSTALL THE NEW WNDHOOK_PROCEDURE **********************
				// *******************************************************************************
				// GWL_WNDPROC - Sets a new address for the window procedure (WinProc())
				// Windows NT/2000/XP: You cannot change this attribute if the window does not 
				// belong to the same process as the calling thread. */The hook is responsible for
				// putting the .dll into the memoryspace of every process. This Dll iterates thru 
				// all the Windows and right here hooks into the WndProc
				////
				log(LOGGING_LOW, pCwp->hwnd,"Installing HookWndProc");

				// We have to call SetWindowLongPtr to install the WNDPROC. Not doins so causes
				// our UnHook message to never be received by the Hooked Wnd
				int result = SetWindowLongPtr( pCwp->hwnd, GWL_WNDPROC, (LONG) MyWndProc );														
			}						
		}	
	}	
	
	// We now pass in our Hook Handle down the current hook chain (in case other hooks are listening)
	return (CallNextHookEx( s_ButtonHookAccess->get_hHook(), nCode, wParam, lParam ));
}
//////////////////////////////////////////////////////////////////////
// Func: Helper_ReadSafeArrayIntoCharArr()
// Desc: Small Helper function to quickly read a SafeArray Into a Char Array
//		 
// Prms: +pDes, char destination buffer to copy into
//		 -size, passes out the size of the safearray
//		 +pSrc, the Safearray source to copy from
//
// Note: Not much safety checking going on. Make sure the buffer has enough space!
//		 ~also only 1 dimensional arrays/safearrays work
//
// IMP!: MAX_PATH is the ultimate Max of size and elements of the pDest Buffer
//////////////////////////////////////////////////////////////////////
void Helper_ReadSafeArrayIntoCharArr(char pDes[MAX_PATH][MAX_PATH],UINT& size,SAFEARRAY* pSrc)
{
	try
	{
		// this func only handles 1-dim arrays/safearrays
		const int ONE_DIMENSIONAL = 1;

		long lUBound = 0;
		long lLBound = 0;
		SafeArrayGetLBound(pSrc, ONE_DIMENSIONAL, &lLBound); 	
		SafeArrayGetUBound(pSrc, ONE_DIMENSIONAL, &lUBound);	
		
		BSTR* pBSTR;		
		SafeArrayAccessData(pSrc, (void**) &pBSTR);
		for (long n = lLBound; ((n <= lUBound) && (n < MAX_PATH)); ++n)
		{
			_bstr_t bstr(pBSTR[n], true);
			strncpy(&pDes[n][0],(char*) bstr , MAX_PATH);			
		}	
		SafeArrayUnaccessData(pSrc);
		
		// pass out the size
		size = lUBound - lLBound + 1;

		// pSrc was empty so set size to 0
		if((size == 1) && (pDes[0][0] == 0))
			size = 0;
	}
	catch(...)
	{
		return;
	}
}
//////////////////////////////////////////////////////////////////////
// Func: ReadOoganizerSettings()
// Desc: Calls OoganizerSettings COM Object to read in all the settings
//		 into our SharedDS. 
//
//		 These are the important settings we need like Logging details,
//		 What windows to hook into, what window classes, and even 
//		 window layout as well as debugging options
//
// Retr: true, if settings were read, false otherwise
//////////////////////////////////////////////////////////////////////
bool ReadOoganizerSettings()
{
	try
	{	
		IOoganizerSettingsPtr pSettings(__uuidof(OoganizerSettingsAcc));

		// Read Configuration File
		_configurationPtr config = NULL;
		if(FAILED(pSettings->ReadConfigXMLFile(&config)))
			return false;


		// Get ButtonHook Settings
		_ButtonHookSettingsPtr buttonhookSettings = NULL;
		if(FAILED(config->get_ButtonHook(&buttonhookSettings)))
			return false;

		SAFEARRAY* safeArray;		

		// Get AllowedProcessNames
		_AllowedProcessNamesWPtr processNames;
		buttonhookSettings->get_AllowedProcessNames(&processNames);
		processNames->get_ProcessName(&safeArray);
		Helper_ReadSafeArrayIntoCharArr(s_ButtonHookAccess->ALLOWED_PROCESS_NAMES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_PROCESS_NAMES,safeArray);

		// Get AllowedWindowTitles
		_AllowedWindowTitlesWPtr windowTitles;	
		buttonhookSettings->get_AllowedWindowTitles(&windowTitles);
		windowTitles->get_WindowTitle(&safeArray);
		Helper_ReadSafeArrayIntoCharArr(s_ButtonHookAccess->ALLOWED_WINDOW_TITLES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_TITLES,safeArray);

		// Get AllowedWindowClasses
		_AllowedWindowClassesWPtr windowClasses;	
		buttonhookSettings->get_AllowedWindowClasses(&windowClasses);
		windowClasses->get_WindowClass(&safeArray);		
		Helper_ReadSafeArrayIntoCharArr(s_ButtonHookAccess->ALLOWED_WINDOW_CLASSES,s_ButtonHookAccess->NUMBER_OF_ALLOWED_WINDOW_CLASSES,safeArray);

		// Get LogDebug Settings
		_bstr_t LogPath;
		_LoggingSettingsPtr logSettings;
		buttonhookSettings->get_LogSettings(&logSettings);
		logSettings->get_LogPath(LogPath.GetAddress());
		s_ButtonHookAccess->SetLogPath((char*) LogPath);		

		unsigned long nLoggingDetail = 0;
		logSettings->get_LoggingDetail(&nLoggingDetail);		
		s_ButtonHookAccess->SetLoggingDetail(static_cast<LoggingDetail>(nLoggingDetail));

		// Now set this instance's Logging option
		SetLoggingDetail(s_ButtonHookAccess->GetLoggingDetail(),s_ButtonHookAccess->GetLogPath());
		
		return true;
	}
	catch(...)
	{
		return false;
	}
}
//////////////////////////////////////////////////////////////////////
// Func: SystemMetricsTesterHELPER()
// Desc: Use this for Debugging only when you need to know certain system metrics values
//////////////////////////////////////////////////////////////////////
void SystemMetricsTesterHELPER()
{
	int k = 0;
	k = GetSystemMetrics(SM_CXEDGE); //2 on vista
	k = GetSystemMetrics(SM_CYEDGE); //2 on vista

	k = GetSystemMetrics(SM_CXFRAME); //8 on vista
	k = GetSystemMetrics(SM_CYFRAME); //8 on vista

	k = GetSystemMetrics(SM_CXFIXEDFRAME); //3 on vista
	k = GetSystemMetrics(SM_CYFIXEDFRAME); //3 on vista

	k = GetSystemMetrics(SM_CXMIN); //124 on vista
	k = GetSystemMetrics(SM_CYMIN); //36 on vista

	k = GetSystemMetrics(SM_CXBORDER); // 1 on vista
	k = GetSystemMetrics(SM_CYBORDER); // 1 on vista

	k = GetSystemMetrics(SM_CXSIZE); //32 on vista
	k = GetSystemMetrics(SM_CYSIZE); //19 on vista

	k = GetSystemMetrics(SM_CYCAPTION); //20 on vista
	//k = GetSystemMetrics(SM_CXCAPTION);

	k = GetSystemMetrics(SM_CYSMCAPTION); //18 on vista
	//k = GetSystemMetrics(SM_CXSMCAPTION);

	k = GetSystemMetrics(SM_CYSMSIZE); //17 on vista
	k = GetSystemMetrics(SM_CXSMSIZE); //17 on vista
}
//////////////////////////////////////////////////////////////////////
// Func: StartWinProcButtonHook()
// Desc: Exported DLL function to allow a process to start/attach 
//		 the system WinProcButtonhook. This function will attach the WinProc function 
//		 to the process's window via the CallWndHookProc() function
//
// Note: We'll check if the system is supported here. We'll only allow
//		 to start the hook, if that is the case
//       
// Retr: TRUE, if succesfully attached, FALSE otherwise (i.e. OS NOT SUPPORTED)
//////////////////////////////////////////////////////////////////////
BOOL WINAPI StartButtonHook(void)	
{	

 // Debugging ONLY when we need certain values (comment/uncomment)
 //SystemMetricsTesterHELPER();

	// Global doesn't exist, we can start
	if(s_ButtonHookAccess->get_hHook() == NULL)
	{
		// Don't start ButtonHook unless the OS supports it
		VARIANT_BOOL bIsSupported;
		_EnvironmentCCWPtr env(__uuidof(EnvironmentCCW));		
		env->IsWindowsOSSupported(&bIsSupported);				

		if(bIsSupported == -1) // TRUE
		{	
			bool bSettings = ReadOoganizerSettings();
			clearLog(); // Start Fresh (with a new Log)

			if(!bSettings) 			
				log(LOGGING_LOW, "ERROR: ReadOoganizerSettings Failed!");							
			else			
				log(LOGGING_HIGH, "ReadOoganizerSettings Succeeded");

			// There really is no sense to start ButtonHook with no Settings Info
			if(bSettings)
			{
				log(LOGGING_LOW, "StartButtonHook Called()");								

				// WPFComWrapper started here for faster loadtime
				ITerminatablePtr wpfCOMCCW;			
				HRESULT hr = wpfCOMCCW.CreateInstance(__uuidof(ButtonWPFormCCW));

				if(SUCCEEDED(hr))
					log(LOGGING_LOW,"ButtonWPFormCCW StartButtonHook() Instantiated successfully");
				else
				{
					log(LOGGING_LOW,"ButtonWPFormCCW StartButtonHook() Instantiation FAILED! Trying again...");
					hr = wpfCOMCCW.CreateInstance(__uuidof(ButtonWPFormCCW));
					if(FAILED(hr))
					{
						log(LOGGING_LOW,"ButtonWPFormCCW StartButtonHook() Instantiation FAILED! Trying again...");
						hr = wpfCOMCCW.CreateInstance(__uuidof(ButtonWPFormCCW));
					}
				}

				if(wpfCOMCCW != NULL)
				{
					// We Instantiate the ButtonWPForm here (optimizing performance)
					wpfCOMCCW->Instantiate();
					// We can release it now (we only want to make sure it is loaded)
					wpfCOMCCW.Release(); 

					// Create/Store Global Hook (every instance needs access to this HOOK)
					// ~WH_CALLWNDPROC hook is called in the context of the thread that calls SendMessage
					log(LOGGING_MEDIUM, "Setting the Windows Hook");
					s_ButtonHookAccess->set_hHook(SetWindowsHookEx( WH_CALLWNDPROC, CallWndHookProc, s_hModule, 0 ));	
							
					// Send Hook Msg - This forces all windows already open in the system and that
					// we want to hook into to load the captionbutton
					EnumWindows( EnumProc, _MSG_HOOK );
					
					return TRUE;
				}
				else
				{
					log(LOGGING_LOW,"ButtonWPFormCCW StartButtonHook() Instantiation FAILED!! Couldn't instantiate wpfCOMCCW in time.");
				}
			}
		}
	}
	
	return FALSE;
}
//////////////////////////////////////////////////////////////////////
// Func: StopWinProcButtonHook()
// Desc: Exported DLL function to allow a process to stop/deattach 
//		 the WinProcButton system hook
// Prms: bUnload = After stopping the Hook, should we unload this DLL?
//       
// Retr: TRUE, if succesfully deattached, FALSE otherwise
//////////////////////////////////////////////////////////////////////
BOOL WINAPI StopButtonHook()
{
	// Global does exist, we can exit
	if(s_ButtonHookAccess->get_hHook() != NULL)
	{	
		log(LOGGING_LOW,"StopButtonHook Called()");						

		// Send UnHook Msg		
		EnumWindows( EnumProc, _MSG_UNHOOK );		
	
		// now we can exit
		if ( UnhookWindowsHookEx( s_ButtonHookAccess->get_hHook() ) != 0 )
		{
			log(LOGGING_LOW,"Good News! - Successfully Unhooked Hook %i", s_ButtonHookAccess->get_hHook());											
		}
		
		// Reset Global Hook
		s_ButtonHookAccess->set_hHook(NULL);		
					
		// Delete All Shared Data
		log(LOGGING_MEDIUM,"Clearing ALL Hook Related Data");
		s_ButtonHookAccess->ClearHookSData();	
		
		// WPFComWrapper started here to let it know we are done
		ITerminatablePtr wpfCOMCCW;			
		HRESULT hr = wpfCOMCCW.CreateInstance(__uuidof(ButtonWPFormCCW));

		if(SUCCEEDED(hr))
			log(LOGGING_LOW,"ButtonWPFormCCW StopButtonHook() Instantiated successfully");
		else
			log(LOGGING_LOW,"ButtonWPFormCCW StopButtonHook() Instantiation FAILED!");

		if(wpfCOMCCW != NULL)
		{
			// We done with it.
			wpfCOMCCW->Terminate();
			wpfCOMCCW.Release(); 
		}

		return TRUE;
	}

	return FALSE;	
}
//////////////////////////////////////////////////////////////////////
// Func: DllMain()
// Desc: Dll Main Entry Point - The DLL is being attached to every
//		 process in the system! ~therefore it must be fast. Also
//		 we can't start the main register hook function from here
//       system call from here. (because this dll get loaded many times,
//		 we only however want the system hook code to be called only once.
//       
// Note: Attaching the dll will cause MSG_HOOK to be registred
//
// Retr: TRUE, always
//////////////////////////////////////////////////////////////////////
BOOL APIENTRY DllMain( HANDLE hModule, DWORD  ul_reason_for_call, LPVOID lpReserved )
{	

    switch ( ul_reason_for_call )
	{
		case DLL_PROCESS_ATTACH:												
			
			// This is to access DS segment's Hook Information,
			// Across all instances of this DLL.
			s_ButtonHookAccess = new CWHPaccess();
					
			// Store local Module Handle
			s_hModule = (HINSTANCE) hModule;
			
			// message is used to send to all windows when the HOOK attaches
			m_WMHook = RegisterWindowMessage( MSG_HOOK );						

			// message is used to send to all windows when the HOOK unattaches			
			m_WMUnHook = RegisterWindowMessage( MSG_UNHOOK );			

			// get the shared setting logging information and store it into this instance
			SetLoggingDetail(s_ButtonHookAccess->GetLoggingDetail(), s_ButtonHookAccess->GetLogPath());

			break;			
		case DLL_PROCESS_DETACH:		
		case DLL_THREAD_DETACH:
		case DLL_THREAD_ATTACH:						
		default:
			break;
    }

    return TRUE;
}